﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;

namespace LogicExpressionCalculator
{
    public class LogicExpression
    {
        #region Properties
        /// <summary>
        /// Operand List
        /// </summary>
        public Hashtable OperandList { get { return _operandList; } }
        /// <summary>
        /// Get Logic Operand based on feature name
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public LogicOperand this[string index] { get { return getOperand(index); } }
        #endregion

        #region Fields

        /// <summary>
        /// Value : Logic Operand
        /// Key: Operand ID
        /// </summary>
        private Hashtable _operandList;
        private List<AbstractExpressionObject> _objList;
        #endregion

        #region Constructor
        /// <summary>
        /// 
        /// </summary>
        public LogicExpression()
        {
            _operandList = new Hashtable();
            _objList = new List<AbstractExpressionObject>();
        }
        #endregion

        /// <summary>
        /// Add to end of Calculate Object List
        /// </summary>
        /// <param name="_objList"></param>
        public void Add(AbstractExpressionObject obj)
        {
            _objList.Add(obj);
        }
        /// <summary>
        /// Add to end of Calculate Object List
        /// </summary>
        /// <param name="_objList"></param>
        public void Add(AbstractExpressionObject[] obj)
        {
            _objList.AddRange(obj);
        }
        /// <summary>
        /// Generate Operand which is used to calculate later
        /// </summary>
        /// <param name="featureName"></param>
        /// <returns></returns>
        public LogicOperand GenerateOperand(string featureName)
        {
            if (_operandList.ContainsKey(featureName))
            {
                throw new Exception("Operand with same id has existed.");
            }
            LogicOperand operand = new LogicOperand(featureName);
            _operandList.Add(featureName, operand);
            return operand;
        }
        /// <summary>
        /// Get the true value List which let the result of logic expression is true.
        /// </summary>
        /// <returns></returns>
        public List<string[]> CalculateLogicSatisfy()
        {
            // store operand list to array
            LogicOperand[] operandList = 
                new LogicOperand[_operandList.Count];
            int index = 0;
            foreach (LogicOperand obj in _operandList.Values)
            {
                operandList[index++] = obj;
            }
            List<string[]> result = new List<string[]>();
            subCalculator(operandList, 0, result);
            return result;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="operandList"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        private void subCalculator(LogicOperand[] operandList
            , int index, List<string[]> result)
        {
            // at the end of operand list, calculate the result of the expression
            if (index == operandList.Length)
            {
                // turn to expression
                Stack<object> express = new Stack<object>();
                for(int i = 0; i != _objList.Count; ++i)
                {
                    if (this._objList[i] is LogicOperand)
                    {
                        LogicOperand lo = (LogicOperand)_objList[i];
                        express.Push(lo.Value);
                    }
                    else if (this._objList[i] is LogicOperator)
                    {
                        LogicOperator lo = (LogicOperator)_objList[i];
                        express.Push(lo.Type);
                    }
                }
                bool ret = ExpressionCalculator.LogicCalculate(express);
                if (ret == true)
                {
                    // add current true list in the result
                    List<string> tmpRet = new List<string>();
                    foreach (LogicOperand lo in operandList)
                    {
                        if (lo.Value == true)
                        {
                            tmpRet.Add(lo.Identify);
                        }
                    }
                    if (tmpRet.Count != 0)
                    {
                        result.Add(tmpRet.ToArray<string>());
                    }
                }
                return;
            }

            operandList[index].Value = true;
            subCalculator(operandList, index + 1, result);
            operandList[index].Value = false;
            subCalculator(operandList, index + 1, result);
        }
        /// <summary>
        /// Get operand
        /// </summary>
        /// <param name="id"></param>
        private LogicOperand getOperand(string featureName)
        {
            return (LogicOperand)_operandList[featureName];
        }
    }
}
